#include <stm32f4xx_rcc.h>
#include <stm32f4xx_gpio.h>
#include <misc.h>
#include <rtthread.h>
#include <spi_flash_sfud.h>
#include <fal.h>
#include <dfs_posix.h>
#include "./SRAM_Size.h"
#include "./DeviceDrivers/drv_spi.h"
#include "./Applications/cpuusage.h"
#include "drivers/spi/spi_msd.h"
#include "dfs/filesystems/elmfat/dfs_elm.h"
#include "./DeviceDrivers/stm32_drv_can.h"
#include "./Applications/led.h"
#include "./Applications/watchdog.h"
#include "./parameter.h"
#include "./Applications/Location.h"
#include "./Applications/Modbus/modbus_slave_core.h"
#include "./Applications/Modbus/RTU/Slave/modbus_slave.h"
#include "./Applications/Modbus/RTU/Master/modbusrtu_master.h"
#include "./Applications/Move_Control.h"
#include "./Applications/Avoid.h"
#include "./Applications/HMI.h"
#include "./Applications/Indication.h"
#include <arm_math.h>

#define LOG_TAG "main"      /* 模块TAG */
#define LOG_LVL LOG_LVL_DBG /* 静态过滤级别 */
#include <ulog.h>           /* 必须放在宏定义下面 */

extern void rt_hw_board_init(void);
static void init_system_rcc(void);

#ifdef RT_DEBUG
static void rtt_user_assert_hook(const char *ex, const char *func, rt_size_t line); /* ASSERT回调函数 */
void rtt_user_assert_hook(const char *ex, const char *func, rt_size_t line)
{
    rt_enter_critical();
    ulog_output(LOG_LVL_ASSERT, "rtt", RT_TRUE, "(%s) has assert failed at %s:%ld.", ex, func, line);
    ulog_flush();
    while (1)
        ;
}
#endif

/**
 * @description: 初始化硬件
 * @param {type} 
 * @return: 
 */
void rt_hw_board_init()
{
    init_system_rcc();                                                     /* 初始化系统时钟 */
    SYSCFG->CMPCR = SYSCFG_CMPCR_CMP_PD;                                   /* IO高速输出时，打开补偿单元 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                        /* 分配中断组 */
    rt_system_heap_init((void *)STM32_SRAM_BEGIN, (void *)STM32_SRAM_END); /* 初始化内存堆 */

#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init(); /* 初始化板级外设 */
#endif

#ifdef RT_USING_CONSOLE
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
}

/**
 * @description: 初始化Systick定时器，外设时钟
 * @param {type} 
 * @return: 
 */
void init_system_rcc(void)
{
    RCC_ClocksTypeDef rcc_clocks;
    rt_uint32_t cnts;
    RCC_GetClocksFreq(&rcc_clocks);
    cnts = (rt_uint32_t)rcc_clocks.HCLK_Frequency / RT_TICK_PER_SECOND;
    cnts = cnts / 8;
    SysTick_Config(cnts); /* 配置SysTick定时器，并打开中断 */
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

    /*复位外设*/
    RCC->AHB1RSTR = (RCC_AHB1RSTR_GPIOARST | RCC_AHB1RSTR_GPIOBRST | RCC_AHB1RSTR_GPIOCRST |
                     RCC_AHB1RSTR_GPIODRST | RCC_AHB1RSTR_GPIOERST | RCC_AHB1RSTR_GPIOFRST | RCC_AHB1RSTR_GPIOGRST |
                     RCC_AHB1RSTR_DMA1RST | RCC_AHB1RSTR_DMA2RST);
    RCC->APB1RSTR = (RCC_APB1RSTR_SPI2RST | RCC_APB1RSTR_SPI3RST |
                     RCC_APB1RSTR_TIM2RST | RCC_APB1RSTR_TIM3RST | RCC_APB1RSTR_TIM4RST | RCC_APB1RSTR_TIM5RST | RCC_APB1RSTR_TIM6RST | RCC_APB1RSTR_TIM7RST | RCC_APB1RSTR_TIM12RST | RCC_APB1RSTR_TIM13RST | RCC_APB1RSTR_TIM14RST |
                     RCC_APB1RSTR_USART2RST | RCC_APB1RSTR_USART3RST | RCC_APB1RSTR_UART4RST | RCC_APB1RSTR_UART5RST |
                     RCC_APB1RSTR_CAN1RST | RCC_APB1RSTR_CAN2RST);
    RCC->APB2RSTR = (RCC_APB2RSTR_SPI1RST |
                     RCC_APB2RSTR_USART1RST | RCC_APB2RSTR_USART6RST |
                     RCC_APB2RSTR_TIM1RST | RCC_APB2RSTR_TIM8RST | RCC_APB2RSTR_TIM9RST | RCC_APB2RSTR_TIM10RST | RCC_APB2RSTR_TIM11RST |
                     RCC_APB2RSTR_SYSCFGRST);

    /*复位结束*/
    RCC->AHB1RSTR = ((uint32_t)0x00000000);
    RCC->APB1RSTR = ((uint32_t)0x00000000);
    RCC->APB2RSTR = ((uint32_t)0x00000000);

    /*
    *   使能外设时钟
    *   TIM6    TL740陀螺仪数据帧检测定时器
    *   TIM7    定位线程时间间隔测量定时器
    *   TIM10   Modbus Master数据帧检测定时器
    *   TIM11   Modbus Slave数据帧检测定时器
    */
    RCC->AHB1ENR = (RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN | /* GPIO口时钟 */
                    RCC_AHB1ENR_DMA1EN | RCC_AHB1ENR_DMA2EN | RCC_AHB1ENR_CCMDATARAMEN                      /* DMA时钟和CCM内存时钟 */
    );
    RCC->APB1ENR = (RCC_APB1ENR_SPI2EN |                                                                      /* SPI时钟 */
                    RCC_APB1ENR_TIM6EN | RCC_APB1ENR_TIM7EN |                                                 /* 定时器时钟 */
                    RCC_APB1ENR_USART2EN | RCC_APB1ENR_USART3EN | RCC_APB1ENR_UART4EN | RCC_APB1ENR_UART5EN | /* 串口时钟 */
                    RCC_APB1ENR_CAN1EN | RCC_APB1ENR_CAN2EN                                                   /* CAN时钟 */
    );
    RCC->APB2ENR = (RCC_APB2ENR_SPI1EN |                        /* SPI1时钟 */
                    RCC_APB2ENR_USART1EN |                      /* 串口时钟 */
                    RCC_APB2ENR_TIM10EN | RCC_APB2ENR_TIM11EN | /* 定时器时钟 */
                    RCC_APB2ENR_SYSCFGEN                        /* 外部中断 */
    );
}

/**
 * @description: 用户main线程
 * @param {type} 
 * @return: 
 */
int main(void)
{
    rt_assert_set_hook(rtt_user_assert_hook); /* 设置用户ASSERT回调函数 */
    cpu_usage_init();                         /* 设置计算CPU使用率函数 */
    Init_Parameter();                         /* 初始化参数列表 */
    led_blink_thread_init();                  /* 初始化LED闪烁线程 */
    watchdog_thread_init();                   /* 初始化硬件看门狗喂狗线程 */

    stm32_spi_bus_attach("spi1", "spi1-sdcard", GPIOA, GPIO_Pin_4);  /* 挂载SD Card */
    stm32_spi_bus_attach("spi2", "spi2-25Q64C", GPIOB, GPIO_Pin_11); /* 挂载W25Q64 */

    msd_init("sd0", "spi1-sdcard");                                   /* 初始化sd0块设备 */
    rt_sfud_flash_probe(FAL_USING_NOR_FLASH_DEV_NAME, "spi2-25Q64C"); /* 根据SFDP标准探测SPI Flash */
    fal_init();                                                       /* 初始化FAL */
    fal_mtd_nor_device_create("littleFS");                            /* 生成 mtd 设备 */

    /* 挂载 littlefs */
    if (0 != dfs_mount("littleFS", "/", "lfs", 0, 0))
    {
        /* 挂载LFS失败 */
        dfs_mkfs("lfs", "littleFS"); /* 格式化LFS */
        if (0 != dfs_mount("littleFS", "/", "lfs", 0, 0))
        {
            /* 挂载LFS失败 */
            LOG_E("Failed to initialize lfs!");
        }
    }
    // mkdir("/sd", 0x777); /* 创建路径，用于挂载SD卡 */

    // dfs_mount("sd0", "/sd", "elm", 0, 0); /* 尝试挂载SD卡 */

    Can_Init(CAN2, CAN1MBaud);                                                         /* 初始化CAN2，波特率1M */
    Modbus_Slave_Core_Init();                                                          /* 初始化Modbus Slave核心线程 */
    MR_Master_Init(115200);                                                            /* 初始化Modbus RTU Master */
    Modbus_RTU_Slave_Thread_Init(agv_parameter->baudrate, agv_parameter->agv_address); /* 初始化Modbus RTU Slave 线程 */
    Avoid_Init(agv_parameter->avoid_slave);                                            /* 避障传感器数据更新线程 */
    Indicate_Init(agv_parameter->indicate_slave);                                      /* 初始化指示线程 */
    hmi_thread_init(agv_parameter->hmi_slave);                                         /* 初始化HMI线程 */
    Location_Thread_Init();                                                            /* 初始化定位线程 */
    Move_Control_Thread_Init();                                                        /* 初始化运动控制线程 */

    rt_thread_delay(1000);
    // ulog_lfs_backend_init(); /* 增加文件系统日志后端 */
}

/* 重启指令 */
static rt_err_t reboot(int argc, char **argv)
{
    /* 关闭全局中断，软件复位 */
    NVIC_SystemReset();
    return RT_EOK;
}
MSH_CMD_EXPORT(reboot, reboot);

static struct ulog_backend lFS; /* little文件后端 */
static char *log_dir = "/log";  /* 存储日志的目录 */
char file_name[32] = {0};

static void ulog_lFS_init(struct ulog_backend *backend)
{
    DIR *dir;
    char dir_name[32] = {0};
    dir = opendir(log_dir);
    if (RT_NULL == dir)
    {
        /* 目录打开失败 */
        mkdir(log_dir, 0x777);  /* 创建目录 */
        dir = opendir(log_dir); /* 打开目录 */
    }

    {
        struct statfs temp;
        struct dirent *d;

        while (1)
        {
            statfs("/", &temp); /* 读取剩余扇区大小 */
            if (temp.f_bfree < 32)
            {
                /* 不足10个扇区 */
                d = readdir(dir);
                if (d == RT_NULL)
                {
                    break;
                }
                sprintf(dir_name, "%s/%s", log_dir, d->d_name);
                int result = rmdir(dir_name); /* 删除目录 */
            }
            else
            {
                break;
            }
        }
    }

    struct tm _time;

    /* 创建当前日期文件夹 */
    time_t now = time(RT_NULL); /* 获取当前时间 */
    gmtime_r(&now, &_time);     /* 获取当前年月日时分 */
    sprintf(dir_name, "%s/%d-%d-%d", log_dir, _time.tm_year + 1900, _time.tm_mon + 1, _time.tm_mday);
    dir = opendir(dir_name);
    if (RT_NULL == dir)
    {
        /* 目录打开失败 */
        mkdir(dir_name, 0x777);  /* 创建目录 */
        dir = opendir(dir_name); /* 打开目录 */
    }

    sprintf(file_name, "%s/%d.log", dir_name, _time.tm_hour);
}

static void ulog_lFS_output(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw,
                            const char *log, size_t len)
{
    if (level > LOG_LVL_INFO)
    {
        return;
    }
    int log_file = open(file_name, O_RDWR | O_CREAT | O_APPEND); /* 读写方式，不存在则建立该文件，数据追加到末尾 */
    if (log_file != -1)
    {
        /* 文件打开成功 */
        write(log_file, log, len); /* 写入数据 */
        close(log_file);           /* 关闭文件 */
    }
}

static void ulog_lFS_Flush(struct ulog_backend *backend)
{
    int log_file = open(file_name, O_RDWR | O_CREAT | O_APPEND); /* 读写方式，不存在则建立该文件，数据追加到末尾 */
    fsync(log_file);
}

void ulog_lfs_backend_init(void)
{
    lFS.init = ulog_lFS_init;
    lFS.output = ulog_lFS_output;
    lFS.flush = ulog_lFS_Flush;
    ulog_backend_register(&lFS, "log", RT_FALSE);
}
